package com.szsh.aiot.hsm.dao;

import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.szsh.aiot.common.utils.DateUtils;
import com.szsh.aiot.common.utils.StringUtils;
import com.szsh.aiot.hsm.constant.Constants;
import com.szsh.aiot.hsm.domain.homeNetWork.NetTopInfo;
import com.szsh.aiot.hsm.domain.homeNetWork.NetTopologicalInfo;
import com.szsh.aiot.hsm.domain.homeNetWork.NetTopologicalInfoResponse;
import com.szsh.aiot.hsm.domain.homeNetWork.TopoInfo;
import com.szsh.aiot.hsm.properties.HomeNetWorkProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Component
public class HomeNetworkDao {
    private static final Logger LOGGER = LoggerFactory.getLogger(HomeNetworkDao.class);

//    private final StringRedisTemplate stringRedisTemplate;

    private final HashOperations<String, String, String> stringHashOperations;

    private final HomeNetWorkProperties homeNetWorkProperties;

    private final RestTemplate restTemplate;

    private final Gson gson;

    public HomeNetworkDao(StringRedisTemplate stringRedisTemplate, HomeNetWorkProperties homeNetWorkProperties, RestTemplate restTemplate, Gson gson) {
//        this.stringRedisTemplate = stringRedisTemplate;
        this.stringHashOperations = stringRedisTemplate.opsForHash();
        this.homeNetWorkProperties = homeNetWorkProperties;
        this.restTemplate = restTemplate;
        this.gson = gson;
    }

    /**
     * 调用拓扑接口，获取最近设备列表<br>
     * 缓存
     * @return 拓扑列表
     */
    public List<TopoInfo> getTopoListFromCache(String gatewayMac) {
        String key = Constants.ALL_TOPO + gatewayMac;
        String json = stringHashOperations.get(key, gatewayMac);
        if (StringUtils.isBlank(json)) {
            return new ArrayList<>();
        }

        List<TopoInfo> topoInfoList = gson.fromJson(json, new TypeToken<List<TopoInfo>>() {}.getType());
        List<TopoInfo> newTopo = new ArrayList<>();
        for (TopoInfo topoInfo : topoInfoList) {
            if (filterTime(topoInfo)) {
                newTopo.add(topoInfo);
            }
        }
        return newTopo;
    }

    /**
     * 调用拓扑接口，获取最近设备列表<br>
     * @return 拓扑列表
     */
    public List<TopoInfo> getTopoList(String gatewayMac) {
        // 查询拓扑接口
        NetTopologicalInfo netTopologicalInfo = getTopologicalInfo(gatewayMac);
        if (null == netTopologicalInfo || null == netTopologicalInfo.getTopoInfo()) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("网关[{}]调用拓扑接口查询设备列表返回失败.", gatewayMac);
            }
            return new ArrayList<>();
        }

        String info = gson.toJson(netTopologicalInfo.getTopoInfo());
        if (StringUtils.isBlank(info)) {
            return new ArrayList<>();
        }
        return gson.fromJson(info, new TypeToken<List<TopoInfo>>() {}.getType());
    }

    public NetTopologicalInfo getTopologicalInfo(String gateMac) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("获取[{}]家庭网络拓扑信息", gateMac);
        }

        JsonObject requestJsonObject = new JsonObject();

        requestJsonObject.addProperty("CmdType", "GET_NET_TOPOLOGICAL_INFO");
        requestJsonObject.addProperty("SequenceId", UUID.randomUUID().toString());

        // 发送请求
        NetTopologicalInfoResponse response = post(homeNetWorkProperties.getGetNetTopologicalInfoUrl(),
                requestJsonObject, NetTopologicalInfoResponse.class, "GET_NET_TOPOLOGICAL_INFO", convertMac(gateMac));

        if (response == null || !"0".equals(response.getStatus())) {
            return null;
        }
        NetTopologicalInfo topologicalInfo = response.getNetTopologicalInfo();
        if (topologicalInfo == null || topologicalInfo.getTopoInfo() == null) {
            return null;
        }
        topologicalInfo.setTopoInfo(getNetTopInfo(topologicalInfo.getTopoInfo()));
        return topologicalInfo;
    }

    /**
     * 执行请求
     * @param url 请求地址
     * @param requestJsonObject 参数
     * @param uriVariables      url参数
     * @return null:调用失败
     */
    private String post(String url, JsonObject requestJsonObject, Object... uriVariables) {
        return post(url, requestJsonObject, String.class, uriVariables);
    }

    /**
     * 执行请求
     * @param url 请求地址
     * @param requestJsonObject 参数
     * @param responseType      返回的类型
     * @param uriVariables      url参数
     * @return null:调用失败
     */
    private <T> T post(String url, JsonObject requestJsonObject, Class<T> responseType, Object... uriVariables) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("家网接口,url:[{}],requestJsonObject:[{}],uriVariables:[{}]", url, requestJsonObject,
                    gson.toJson(uriVariables));
        }

        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.add("X-APP-ID", homeNetWorkProperties.getAppId());
        headers.add("X-APP-KEY", homeNetWorkProperties.getAppKey());
        headers.add("X-CTG-Request-Id", UUID.randomUUID().toString());

        HttpEntity<JsonObject> httpEntity = new HttpEntity<>(requestJsonObject, headers);

        // 发送请求
        T resultObject;
        try {
            resultObject = restTemplate.postForObject(url, httpEntity, responseType, uriVariables);
        } catch (Exception e) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("家网接口,调用接口异常", e);
            }
            return null;
        }
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("家网接口,返回结果:[{}]", resultObject);
        }

        return resultObject;
    }


    /**
     * 保留7天内的在线设备
     * @param topoInfo  拓扑信息
     * @return true:保留;<br>false:不保留
     */
    private boolean filterTime(TopoInfo topoInfo) {
        if (topoInfo.getActive() == 1) {
            return true;
        }
        String lastActiveTime = topoInfo.getLastActiveTime();
        String lastInactiveTime = topoInfo.getLastInactiveTime();
        boolean flag = false;
        Date now = DateUtils.getNowDate();
        if (StringUtils.isNotBlank(lastActiveTime)) {
            Date activeTime = DateUtils.parseDate(lastActiveTime);
            if (null != activeTime) {
                flag = DateUtils.differentDaysByMillisecond(activeTime, now) <= 7;
            }
        }
        if (StringUtils.isNotBlank(lastInactiveTime) && !flag) {
            Date inactiveTime = DateUtils.parseDate(lastInactiveTime);
            if (null != inactiveTime) {
                flag = DateUtils.differentDaysByMillisecond(inactiveTime, now) <= 7;
            }
        }
        return flag;
    }

    /**
     * MAC地址转换，全部大写，无中间分隔符号
     *
     * @param mac mac地址
     * @return 转换后mac
     */
    private String convertMac(String mac) {
        if (StringUtils.isBlank(mac)) {
            return null;
        } else if (mac.contains(":")) {
            mac = mac.replace(":", "");
        } else if (mac.contains("-")) {
            mac = mac.replace("-", ":");
        }
        return mac.toUpperCase();
    }

    /**
     * 拓扑信息处理
     *
     * @param topoInfo 拓扑信息
     * @return 处理后拓扑信息
     */
    private List<NetTopInfo> getNetTopInfo(Object topoInfo) {
        // 处理topo_info节点，统一返回list
        if (topoInfo instanceof List) {
            return gson.fromJson(gson.toJson(topoInfo), new TypeToken<List<NetTopInfo>>(){}.getType());
        } else {
            List<NetTopInfo> topoList = new ArrayList<>();
            NetTopInfo info = gson.fromJson(gson.toJson(topoInfo), NetTopInfo.class);
            if (null != info) {
                topoList.add(info);
            }
            return topoList;
        }
    }
}
